﻿using Microsoft.Practices.Prism.Commands;
using System;
using System.Windows.Input;
using System.Windows.Threading;
using System.Xml.Serialization;
using System.Threading.Tasks;

namespace HobSoft.SoccerBet2014.Client.ViewModels
{
    using Common;
    using Domain.ClientService;
    using dm = Domain.Models;
    [Serializable]
    [XmlRoot("bet-info")]
    public class BetInfo : CommandViewModel, IDisposable
    {
        public event EventHandler<dm.Site.BettingResult> OnBettingOver;

        #region command
        private ICommand _autoUpdateCommand;
        [XmlIgnore]
        public ICommand AutoUpdateCommand
        {
            get { return _autoUpdateCommand; }
            protected set { _autoUpdateCommand = value; }
        }


        private ICommand _focusCommand;
        /// <summary>
        /// BetInfo设为焦点
        /// </summary>
        [XmlIgnore]
        public ICommand FocusCommand
        {
            get { return _focusCommand; }
            protected set { _focusCommand = value; }
        }

        private ICommand _betMoneyCommand;
        /// <summary>
        /// 投注命令
        /// </summary>
        [XmlIgnore]
        public ICommand BetMoneyCommand
        {
            get { return _betMoneyCommand; }
            protected set { _betMoneyCommand = value; }
        }
        #endregion

        private object _syncLock = new object();
        private bool _enableAutoUpdate = false;
        private DateTime? _lastBettingTime;
        private bool _enableBetting;
        private int updateCountdownMax = 0;
        private int resetCountdownMax = 0;

        protected DispatcherTimer AutoTimer { get; set; }

        private IBetSiteProxyService siteManager;
        public IBetSiteProxyService SiteManager
        {
            get
            {
                return siteManager;
            }
            set
            {
                siteManager = value;
            }
        }

        private Factory.IViewModelFactory viewModelFactory;
        public Factory.IViewModelFactory ViewModelFactory
        {
            get { return viewModelFactory; }
            set { viewModelFactory = value; }
        }

        protected OrderShellModel orderShellModel;

        public BetInfo(IBetSiteProxyService _siteManager, Factory.IViewModelFactory _viewModelFactory)
        {
            siteManager = _siteManager;
            viewModelFactory = _viewModelFactory;
            orderShellModel = viewModelFactory.CreateOrGet<OrderShellModel>();
            FocusCommand = new DelegateCommand(() => IsFocus = true);
            BetMoneyCommand = new DelegateCommand<decimal?>(BetMoney);
            AutoUpdateCommand = new DelegateCommand<bool?>(AutoUpdate);

            RegisterCommand(FocusCommand, "设置焦点");
            RegisterCommand(BetMoneyCommand, "投注");
            RegisterCommand(AutoUpdateCommand, "自动更新设置");
            AutoTimer = new DispatcherTimer();
            AutoTimer.Tick += Timer_Tick;
            AutoTimer.Interval = TimeSpan.FromSeconds(1);
            updateCountdownMax = Properties.Settings.Default.VM_BetInfo_UpdateCountDown;
            resetCountdownMax = Properties.Settings.Default.VM_BetInfo_ResetCountDown;
            EnableBetting = true;
        }

        #region event handler&raise event
        private void Timer_Tick(object sender, EventArgs e)
        {
            if (SiteManager == null || LastTick == null) return;
            AutoTimer.Stop();
            UpdateCountdown++;
            ResetCountdown++;
            var lastCheck = DateTime.Now - _lastBettingTime;
            if (lastCheck.HasValue && lastCheck.Value.TotalMilliseconds > Properties.Settings.Default.VM_BetInfo_LastBettingCheck)
            {
                lock (_syncLock)
                {
                    EnableBetting = true;
                }
            }

            if (UpdateCountdown >= updateCountdownMax)
            {
                ClearMessage();
                UpdateCountdown = 0;
                bool reset = false;
                if (ResetCountdown >= resetCountdownMax)
                {
                    reset = true;
                    ResetCountdown = 0;
                }

                var task = Task.Factory.StartNew(() => SiteManager.RequestTick(LastTick, reset));
                task.ContinueWith(t =>
                {
                    BetApplication.Current.Dispatcher.Invoke(new Action(delegate()
                    {
                        if(t.IsCanceled||t.Exception!=null)
                        {
                            if (t.Exception != null)
                            {
                                RaiseCommandError(t.Exception.InnerException);
                            }
                            return;
                        }
                        if (t.Result.Result == null) return;
                        LastTick = t.Result.Result;
                        if (LastTick.Handicap != this.Handicap)
                            RaiseMessage("更新Ticks", "盘口变更！");
                        this.AwayScore = LastTick.AwayScore;
                        this.HomeScore = LastTick.HomeScore;
                        this.Handicap = LastTick.Handicap;
                        this.MaxBet = LastTick.MaxAmount;
                        this.MinBet = LastTick.MinAmount;
                        this.OddsValue = LastTick.Price;
                        if (_enableAutoUpdate)
                            AutoTimer.Start();
                    }));
                });
            }
            else
            {
                if (_enableAutoUpdate)
                    AutoTimer.Start();
            }

        }

        protected void RaiseOnBettingOver(dm.Site.BettingResult result)
        {
            if (OnBettingOver != null)
                OnBettingOver(this, result);
        }
        #endregion

        #region imple command
        public void BetMoney(decimal? money)
        {
            Money = money;
            if (!Money.HasValue)
                return;
            if (SiteManager == null || LastTick == null) return;
            if (!EnableBetting)
            {
                lock (_syncLock)
                {
                    if (!EnableBetting) return;
                    EnableBetting = false;
                }
            }
            RaiseCommandStart(BetMoneyCommand, "投注请求开始");
            EnableBetting = false;
            _lastBettingTime = DateTime.Now;
            SiteManager.RequestBetting(LastTick, Money.Value).ContinueWith(t =>
            {
                BetApplication.Current.Dispatcher.Invoke(new Action(delegate()
                {
                    RaiseCommandOver(BetMoneyCommand);
                    if (t.Exception != null)
                    {
                        PublicLog.ErrorFormat("投注发生错误：{0}", t.Exception.InnerException.Message);
                    }
                    var result = t.Result;
                    if (result == null)
                    {
                        PublicLog.ErrorFormat("投注没有返回结果");
                    }

                    if (t.IsCompleted && t.Exception == null)
                    {
                        if (result.State == dm.Site.BettingState.Succeed || result.State == dm.Site.BettingState.ToOrder)
                            RaiseMessage("投注", "投注成功");
                        else
                            RaiseMessage("投注", result.Message);
                        RaiseOnBettingOver(result);
                        if (orderShellModel != null)
                            orderShellModel.QueryBmOrder();
                    }
                }));
            });

        }

        public void AutoUpdate(bool? start)
        {
            if (start.HasValue && start.Value)
            {
                if (!_enableAutoUpdate)
                {
                    _enableAutoUpdate = true;
                    RaisePropertyChanged("EnableAutoUpdate");
                    AutoTimer.Start();
                }
            }
            else
            {
                AutoTimer.Stop();
                _enableAutoUpdate = false;
                RaisePropertyChanged("EnableAutoUpdate");
            }
        }
        #endregion

        #region status property
        [XmlIgnore]
        public bool EnableAutoUpdate
        {
            get
            {
                return _enableAutoUpdate;
            }
            protected set
            {
                PropertyValueChange(ref _enableAutoUpdate, value, "EnableAutoUpdate");
            }
        }
        [XmlIgnore]
        public bool EnableBetting
        {
            get
            {
                return _enableBetting;
            }
            protected set
            {
                PropertyValueChange(ref _enableBetting, value, "EnableBetting");
            }
        }

        private bool _isFocus;
        [XmlIgnore]
        public bool IsFocus
        {
            get { return _isFocus; }
            set { PropertyValueChange(ref _isFocus, value, "IsFocus"); }
        }

        private int _updateCountdown;
        [XmlIgnore]
        public int UpdateCountdown
        {
            get { return _updateCountdown; }
            set { PropertyValueChange(ref _updateCountdown, value, "UpdateCountdown"); }
        }

        private int _resetCountdown;
        [XmlIgnore]
        public int ResetCountdown
        {
            get { return _resetCountdown; }
            set { PropertyValueChange(ref _resetCountdown, value, "ResetCountdown"); }
        }
        #endregion

        #region property
        private decimal? _money;
        [XmlIgnore]
        public decimal? Money
        {
            get { return _money; }
            set { PropertyValueChange(ref _money, value, "Money"); }
        }

        private dm.BetTick _lastTick;
        [XmlIgnore]
        public dm.BetTick LastTick
        {
            get { return _lastTick; }
            set { _lastTick = value; }
        }


        private string _gameID;
        [XmlAttribute("game-id")]
        public string GameID
        {
            get { return _gameID; }
            set { PropertyValueChange(ref _gameID, value, "GameID"); }
        }

        private byte _bmID;
        [XmlAttribute("bm-id")]
        public byte BmID
        {
            get { return _bmID; }
            set { PropertyValueChange(ref _bmID, value, "BmID"); }
        }

        private string _league;
        [XmlAttribute("league")]
        public string League
        {
            get { return _league; }
            set { PropertyValueChange(ref _league, value, "League"); }
        }

        private string _homeTeam;
        [XmlAttribute("home-team")]
        public string HomeTeam
        {
            get { return _homeTeam; }
            set { PropertyValueChange(ref _homeTeam, value, "HomeTeam"); }
        }

        private string _awayTeam;
        [XmlAttribute("away-team")]
        public string AwayTeam
        {
            get { return _awayTeam; }
            set { PropertyValueChange(ref _awayTeam, value, "AwayTeam"); }
        }

        private byte _homeScore;
        [XmlAttribute("home-score")]
        public byte HomeScore
        {
            get { return _homeScore; }
            set { PropertyValueChange(ref _homeScore, value, "HomeScore"); }
        }

        private byte _awayScore;
        [XmlAttribute("away-score")]
        public byte AwayScore
        {
            get { return _awayScore; }
            set { PropertyValueChange(ref _awayScore, value, "AwayScore"); }
        }

        private string _rawTitle;
        [XmlAttribute("raw-title")]
        public string RawTitle
        {
            get { return _rawTitle; }
            set { PropertyValueChange(ref _rawTitle, value, "RawTitle"); }
        }

        private dm.Odds.MarketType _type;
        [XmlAttribute("type")]
        public dm.Odds.MarketType Type
        {
            get { return _type; }
            set
            {
                PropertyValueChange(ref _type, value, "Type");
                RaisePropertyChanged("BetToDisplay");
            }
        }

        private BetItem _betTo;
        [XmlAttribute("bet-to")]
        public BetItem BetTo
        {
            get { return _betTo; }
            set
            {
                PropertyValueChange(ref _betTo, value, "BetTo");
                switch (Type)
                {
                    case dm.Odds.MarketType.FH1X2:
                    case dm.Odds.MarketType.G1X2:
                        if (_betTo == BetItem.HomeOrOver)
                            _betToDisplay = () => string.Format("{0}胜", HomeTeam);
                        else if (_betTo == BetItem.AwayOrUnder)
                            _betToDisplay = () => string.Format("{0}胜", AwayTeam);
                        else
                            _betToDisplay = () => "平";
                        break;
                    case dm.Odds.MarketType.FHHDP:
                    case dm.Odds.MarketType.GHDP:
                        if (_betTo == BetItem.HomeOrOver)
                            _betToDisplay = () => HomeTeam;
                        else if (_betTo == BetItem.AwayOrUnder)
                            _betToDisplay = () => AwayTeam;
                        break;
                    case dm.Odds.MarketType.FHOU:
                    case dm.Odds.MarketType.GOU:
                        if (_betTo == BetItem.HomeOrOver)
                            _betToDisplay = () => "大";
                        else if (_betTo == BetItem.AwayOrUnder)
                            _betToDisplay = () => "小";
                        break;
                    default:
                        break;
                }
                RaisePropertyChanged("BetToDisplay");
            }
        }

        private Func<string> _betToDisplay;
        [XmlIgnore]
        public string BetToDisplay
        {
            get
            {
                if (_betToDisplay != null)
                    return _betToDisplay();
                else
                    return string.Empty;
            }
        }

        private decimal _oddsValue;
        [XmlAttribute("odds-value")]
        public decimal OddsValue
        {
            get { return _oddsValue; }
            set { PropertyValueChange(ref _oddsValue, value, "OddsValue"); }
        }

        private decimal _handicap;
        [XmlAttribute("handicap")]
        public decimal Handicap
        {
            get { return _handicap; }
            set
            {
                PropertyValueChange(ref _handicap, value, "Handicap");
                RaisePropertyChanged("HandicapDisplay");
            }
        }

        [XmlIgnore]
        public string HandicapDisplay
        {
            get
            {
                if (Type == dm.Odds.MarketType.G1X2 || Type == dm.Odds.MarketType.FH1X2)
                    return string.Empty;
                else
                    return Handicap.ToString("F2");
            }
        }

        private decimal _minBet;
        [XmlAttribute("min-bet")]
        public decimal MinBet
        {
            get { return _minBet; }
            set { PropertyValueChange(ref _minBet, value, "MinBet"); }
        }

        private decimal _maxBet;
        [XmlAttribute("max-bet")]
        public decimal MaxBet
        {
            get { return _maxBet; }
            set { PropertyValueChange(ref _maxBet, value, "MaxBet"); }
        }
        #endregion
        public void Dispose()
        {
            if (AutoTimer != null)
            {
                if (AutoTimer.IsEnabled)
                    AutoTimer.Stop();
            }
        }
    }

    public enum BetItem
    {
        Tie = 0,
        HomeOrOver = 1,
        AwayOrUnder = 2
    }
}
